{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 异常\n",
    "\n",
    "## 目标\n",
    "\n",
    "* 异常的概念\n",
    "* 捕获异常\n",
    "* 异常的传递\n",
    "* 抛出异常\n",
    "\n",
    "## 01. 异常的概念\n",
    "\n",
    "* 程序在运行时，如果 `Python 解释器` **遇到** 到一个错误，**会停止程序的执行，并且提示一些错误信息**，这就是 **异常**\n",
    "* **程序停止执行并且提示错误信息** 这个动作，我们通常称之为：**抛出(raise)异常**\n",
    "\n",
    "![001_异常示意图-w480](./res/001_%E5%BC%82%E5%B8%B8%E7%A4%BA%E6%84%8F%E5%9B%BE.png)\n",
    "\n",
    "> 程序开发时，很难将 **所有的特殊情况** 都处理的面面俱到，通过 **异常捕获** 可以针对突发事件做集中的处理，从而保证程序的 **稳定性和健壮性**\n",
    "\n",
    "## 02. 捕获异常\n",
    "\n",
    "### 2.1 简单的捕获异常语法\n",
    "\n",
    "* 在程序开发中，如果 **对某些代码的执行不能确定是否正确**，可以增加 `try(尝试)` 来 **捕获异常**\n",
    "* 捕获异常最简单的语法格式：\n",
    "\n",
    "```python\n",
    "try:\n",
    "    尝试执行的代码\n",
    "except:\n",
    "    出现错误的处理\n",
    "```\n",
    "\n",
    "* `try` **尝试**，下方编写要尝试代码，不确定是否能够正常执行的代码\n",
    "* `except` **如果不是**，下方编写尝试失败的代码\n",
    "\n",
    "\n",
    "#### 简单异常捕获演练 —— 要求用户输入整数\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "请输入数字：dhuksaj\n",
      "请输入正确的数字\n"
     ]
    }
   ],
   "source": [
    "try:\n",
    "    # 提示用户输入一个数字\n",
    "    num = int(input(\"请输入数字：\"))\n",
    "except:\n",
    "    print(\"请输入正确的数字\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 2.2 错误类型捕获\n",
    "\n",
    "* 在程序执行时，可能会遇到 **不同类型的异常**，并且需要 **针对不同类型的异常，做出不同的响应**，这个时候，就需要捕获错误类型了\n",
    "\n",
    "* 语法如下：\n",
    "```python\n",
    "try:\n",
    "    # 尝试执行的代码\n",
    "    pass\n",
    "except 错误类型1:\n",
    "    # 针对错误类型1，对应的代码处理\n",
    "    pass\n",
    "except (错误类型2, 错误类型3):\n",
    "    # 针对错误类型2 和 3，对应的代码处理\n",
    "    pass\n",
    "except Exception as result:\n",
    "    print(\"未知错误 %s\" % result)\n",
    "```\n",
    "\n",
    "* 当 `Python` 解释器 **抛出异常** 时，**最后一行错误信息的第一个单词，就是错误类型**\n",
    "\n",
    "#### 异常类型捕获演练 —— 要求用户输入整数\n",
    "\n",
    "**需求**\n",
    "\n",
    "1. 提示用户输入一个整数\n",
    "2. 使用 `8` 除以用户输入的整数并且输出\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "请输入整数：dsahdl\n",
      "请输入正确的整数\n"
     ]
    }
   ],
   "source": [
    "try:\n",
    "    num = int(input(\"请输入整数：\"))\n",
    "    result = 8 / num\n",
    "    print(result)\n",
    "except ValueError:\n",
    "    print(\"请输入正确的整数\")\n",
    "except ZeroDivisionError:\n",
    "    print(\"除 0 错误\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 捕获未知错误\n",
    "\n",
    "* 在开发时，**要预判到所有可能出现的错误**，还是有一定难度的\n",
    "* 如果希望程序 **无论出现任何错误**，都不会因为 `Python` 解释器 **抛出异常而被终止**，可以再增加一个 `except`\n",
    "\n",
    "语法如下：\n",
    "```python\n",
    "except Exception as result:\n",
    "    print(\"未知错误 %s\" % result)\n",
    "```\n",
    "\n",
    "### 2.3 异常捕获完整语法\n",
    "\n",
    "* 在实际开发中，为了能够处理复杂的异常情况，完整的异常语法如下：\n",
    "\n",
    "> 提示：\n",
    "> \n",
    "> * 有关完整语法的应用场景，在后续学习中，**结合实际的案例**会更好理解\n",
    "> * 现在先对这个语法结构有个印象即可\n",
    "\n",
    "```python\n",
    "try:\n",
    "    # 尝试执行的代码\n",
    "    pass\n",
    "except 错误类型1:\n",
    "    # 针对错误类型1，对应的代码处理\n",
    "    pass\n",
    "except 错误类型2:\n",
    "    # 针对错误类型2，对应的代码处理\n",
    "    pass\n",
    "except (错误类型3, 错误类型4):\n",
    "    # 针对错误类型3 和 4，对应的代码处理\n",
    "    pass\n",
    "except Exception as result:\n",
    "    # 打印错误信息\n",
    "    print(result)\n",
    "else:\n",
    "    # 没有异常才会执行的代码\n",
    "    pass\n",
    "finally:\n",
    "    # 无论是否有异常，都会执行的代码\n",
    "    print(\"无论是否有异常，都会执行的代码\")\n",
    "```\n",
    "\n",
    "* `else` 只有在没有异常时才会执行的代码\n",
    "* `finally` 无论是否有异常，都会执行的代码\n",
    "\n",
    "* 之前一个演练的 **完整捕获异常** 的代码如下：\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "请输入整数：dsjalkd\n",
      "请输入正确的整数\n",
      "执行完成，但是不保证正确\n"
     ]
    }
   ],
   "source": [
    "try:\n",
    "    num = int(input(\"请输入整数：\"))\n",
    "    result = 8 / num\n",
    "    print(result)\n",
    "except ValueError:\n",
    "    print(\"请输入正确的整数\")\n",
    "except ZeroDivisionError:\n",
    "    print(\"除 0 错误\")\n",
    "except Exception as result:\n",
    "    print(\"未知错误 %s\" % result)\n",
    "else:\n",
    "    print(\"正常执行\")\n",
    "finally:\n",
    "    print(\"执行完成，但是不保证正确\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 03. 异常的传递\n",
    "\n",
    "* **异常的传递** —— 当 **函数/方法** 执行 **出现异常**，会 **将异常传递** 给 函数/方法 的 **调用一方**\n",
    "* 如果 **传递到主程序**，仍然 **没有异常处理**，程序才会被终止\n",
    "\n",
    "> 提示\n",
    "\n",
    "* 在开发中，可以在主函数中增加 **异常捕获**\n",
    "* 而在主函数中调用的其他函数，只要出现异常，都会传递到主函数的 **异常捕获** 中\n",
    "* 这样就不需要在代码中，增加大量的 **异常捕获**，能够保证代码的整洁\n",
    "\n",
    "**需求**\n",
    "\n",
    "1. 定义函数 `demo1()` **提示用户输入一个整数并且返回**\n",
    "2. 定义函数 `demo2()` 调用 `demo1()`\n",
    "3. 在主程序中调用 `demo2()`\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "请输入一个整数：dsjklad\n",
      "请输入正确的整数\n"
     ]
    }
   ],
   "source": [
    "def demo1():\n",
    "    return int(input(\"请输入一个整数：\"))\n",
    "\n",
    "\n",
    "def demo2():\n",
    "    return demo1()\n",
    "\n",
    "try:\n",
    "    print(demo2())\n",
    "except ValueError:\n",
    "    print(\"请输入正确的整数\")\n",
    "except Exception as result:\n",
    "    print(\"未知错误 %s\" % result)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 04. 抛出 `raise` 异常\n",
    "\n",
    "### 4.1 应用场景\n",
    "\n",
    "* 在开发中，除了 **代码执行出错** `Python` 解释器会 **抛出** 异常之外\n",
    "* 还可以根据 **应用程序** **特有的业务需求** **主动抛出异常**\n",
    "\n",
    "**示例**\n",
    "\n",
    "* 提示用户 **输入密码**，如果 **长度少于 8**，抛出 **异常**\n",
    "\n",
    "![024_自定义异常-w822](./res/024_%E8%87%AA%E5%AE%9A%E4%B9%89%E5%BC%82%E5%B8%B8.png)\n",
    "\n",
    "**注意**\n",
    "\n",
    "* 当前函数 **只负责** 提示用户输入密码，如果 **密码长度不正确，需要其他的函数进行额外处理**\n",
    "* 因此可以 **抛出异常**，由其他需要处理的函数 **捕获异常**\n",
    "\n",
    "### 4.2 抛出异常\n",
    "\n",
    "* `Python` 中提供了一个 `Exception` **异常类**\n",
    "* 在开发时，如果满足 **特定业务需求时**，希望 **抛出异常**，可以：\n",
    "    1. **创建** 一个 `Exception` 的 **对象**\n",
    "    2. 使用 `raise` **关键字** 抛出 **异常对象**\n",
    "\n",
    "**需求**\n",
    "\n",
    "* 定义 `input_password` 函数，提示用户输入密码\n",
    "* 如果用户输入长度 < 8，抛出异常\n",
    "* 如果用户输入长度 >=8，返回输入的密码\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "请输入密码：jdaskl\n",
      "发现错误：密码长度不够\n"
     ]
    }
   ],
   "source": [
    "def input_password():\n",
    "\n",
    "    # 1. 提示用户输入密码\n",
    "    pwd = input(\"请输入密码：\")\n",
    "\n",
    "    # 2. 判断密码长度，如果长度 >= 8，返回用户输入的密码\n",
    "    if len(pwd) >= 8:\n",
    "        return pwd\n",
    "\n",
    "    # 3. 密码长度不够，需要抛出异常\n",
    "    # 1> 创建异常对象 - 使用异常的错误信息字符串作为参数\n",
    "    ex = Exception(\"密码长度不够\")\n",
    "\n",
    "    # 2> 抛出异常对象\n",
    "    raise ex\n",
    "\n",
    "\n",
    "try:\n",
    "    user_pwd = input_password()\n",
    "    print(user_pwd)\n",
    "except Exception as result:\n",
    "    print(\"发现错误：%s\" % result)\n"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.2"
  },
  "toc": {
   "base_numbering": 1,
   "nav_menu": {},
   "number_sections": true,
   "sideBar": true,
   "skip_h1_title": false,
   "title_cell": "Table of Contents",
   "title_sidebar": "Contents",
   "toc_cell": false,
   "toc_position": {},
   "toc_section_display": true,
   "toc_window_display": false
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
